home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / nethack.lha / nethack-3.1 / win / X11 / winmesg.c < prev    next >
C/C++ Source or Header  |  1993-01-16  |  17KB  |  597 lines

  1. /*    SCCS Id: @(#)winmesg.c    3.1    92/05/19          */
  2. /* Copyright (c) Dean Luick, 1992                  */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5. /*
  6.  * Message window routines.
  7.  *
  8.  * Global functions:
  9.  *    set_message_height()
  10.  *    create_message_window()
  11.  *    destroy_message_window()
  12.  *    display_message_window()
  13.  *    append_message()
  14.  */
  15. #include <X11/Intrinsic.h>
  16. #include <X11/StringDefs.h>
  17. #include <X11/Shell.h>
  18. #include <X11/Xaw/Cardinals.h>
  19. #include <X11/Xaw/Viewport.h>
  20. #include "Window.h"    /* Window widget declarations */
  21.  
  22. #include "hack.h"
  23. #include "winX.h"
  24.  
  25. static const char message_translations[] =
  26.     "#override\n\
  27.     <Key>: no-op()";
  28.  
  29. static struct line_element *get_previous();
  30. static void set_circle_buf();
  31. static char *split();
  32. static void add_line();
  33. static void redraw_message_window();
  34. static void mesg_check_size_change();
  35. static void mesg_exposed();
  36. static void get_gc();
  37. static void mesg_resized();
  38.  
  39. /* Adjust the number of rows on the given message window. */
  40. void
  41. set_message_height(wp, rows)
  42.     struct xwindow *wp;
  43.     Dimension rows;
  44. {
  45.     Arg args[1];
  46.  
  47.     wp->pixel_height = wp->mesg_information->char_height * rows;
  48.  
  49.     XtSetArg(args[0], XtNheight, wp->pixel_height);
  50.     XtSetValues(wp->w, args, ONE);
  51. }
  52.  
  53. /* Move the message window's vertical scrollbar's slider to the bottom. */
  54. void
  55. set_message_slider(wp)
  56.     struct xwindow *wp;
  57. {
  58.     Widget scrollbar;
  59.     float top;
  60.  
  61.     scrollbar = XtNameToWidget(XtParent(wp->w), "vertical");
  62.  
  63.     if (scrollbar) {
  64.     top = 1.0;
  65.     XtCallCallbacks(scrollbar, XtNjumpProc, &top);
  66.     }
  67. }
  68.  
  69.  
  70. void
  71. create_message_window(wp, create_popup, parent)
  72.     struct xwindow *wp;            /* window pointer */
  73.     boolean create_popup;
  74.     Widget parent;
  75. {
  76.     Arg args[8];
  77.     Cardinal num_args;
  78.     Widget viewport;
  79.     struct mesg_info_t *mesg_info;
  80.  
  81.     wp->type = NHW_MESSAGE;
  82.  
  83.     wp->mesg_information = mesg_info =
  84.             (struct mesg_info_t *) alloc(sizeof(struct mesg_info_t));
  85.  
  86.     mesg_info->fs = 0;
  87.     mesg_info->num_lines = 0;
  88.     mesg_info->head = mesg_info->last_pause =
  89.             mesg_info->last_pause_head = (struct line_element *) 0;
  90.     mesg_info->dirty = False;
  91.     mesg_info->viewport_width = mesg_info->viewport_height = 0;
  92.  
  93.     /*
  94.      * We should have an .Xdefaults option that specifies the number of lines
  95.      * to be displayed.  Until then, we'll use DEFAULT_LINES_DISPLAYED.
  96.      * E.g.:
  97.      *
  98.      *    if (a lines value from .Xdefaults exists)
  99.      *        lines_displayed = lines value from .Xdefaults;
  100.      *    else
  101.      *        lines_displayed = DEFAULT_LINES_DISPLAYED;
  102.      */
  103.     if (flags.msg_history < DEFAULT_LINES_DISPLAYED)
  104.     flags.msg_history = DEFAULT_LINES_DISPLAYED;
  105.     if (flags.msg_history > MAX_HISTORY)    /* a sanity check */
  106.     flags.msg_history = MAX_HISTORY;
  107.  
  108.     set_circle_buf(mesg_info, (int) flags.msg_history);
  109.  
  110.     /* Create a popup that becomes the parent. */
  111.     if (create_popup) {
  112.     num_args = 0;
  113.     XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
  114.  
  115.     wp->popup = parent = XtCreatePopupShell("message_popup",
  116.                     topLevelShellWidgetClass,
  117.                     toplevel, args, num_args);
  118.     }
  119.  
  120.     /*
  121.      * Create the viewport.  We only want the vertical scroll bar ever to be
  122.      * visible.  If we allow the horizontal scrollbar to be visible it will
  123.      * always be visible, due to the stupid way the Athena viewport operates.
  124.      */
  125.     num_args = 0;
  126.     XtSetArg(args[num_args], XtNallowVert,  True);      num_args++;
  127.     viewport = XtCreateManagedWidget(
  128.                         "mesg_viewport",         /* name */
  129.                         viewportWidgetClass,    /* widget class from Window.h */
  130.                         parent,                 /* parent widget */
  131.                         args,                   /* set some values */
  132.                         num_args);              /* number of values to set */
  133.  
  134.     /*
  135.      * Create a message window.  We will change the width and height once
  136.      * we know what font we are using.
  137.      */
  138.     num_args = 0;
  139.     XtSetArg(args[num_args], XtNtranslations,
  140.     XtParseTranslationTable(message_translations));    num_args++;
  141.     wp->w = XtCreateManagedWidget(
  142.         "message",        /* name */
  143.         windowWidgetClass,    /* widget class from Window.h */
  144.         viewport,        /* parent widget */
  145.         args,            /* set some values */
  146.         num_args);        /* number of values to set */
  147.  
  148.     XtAddCallback(wp->w, XtNexposeCallback, mesg_exposed, (XtPointer) 0);
  149.  
  150.     /*
  151.      * Now adjust the height and width of the message window so that it
  152.      * is DEFAULT_LINES_DISPLAYED high and DEFAULT_MESSAGE_WIDTH wide.
  153.      */
  154.  
  155.     /* Get the font information. */
  156.     num_args = 0;
  157.     XtSetArg(args[num_args], XtNfont, &mesg_info->fs);           num_args++;
  158.     XtGetValues(wp->w, args, num_args);
  159.  
  160.     /* Save character information for fast use later. */
  161.     mesg_info->char_width    = mesg_info->fs->max_bounds.width;
  162.     mesg_info->char_height   = mesg_info->fs->max_bounds.ascent +
  163.                         mesg_info->fs->max_bounds.descent;
  164.     mesg_info->char_ascent   = mesg_info->fs->max_bounds.ascent;
  165.     mesg_info->char_lbearing = -mesg_info->fs->min_bounds.lbearing;
  166.  
  167.     get_gc(wp->w, mesg_info);
  168.  
  169.     wp->pixel_height = DEFAULT_LINES_DISPLAYED * mesg_info->char_height;
  170.  
  171.     /* If a variable spaced font, only use 2/3 of the default size */
  172.     if (mesg_info->fs->min_bounds.width != mesg_info->fs->max_bounds.width) {
  173.     wp->pixel_width  = ((2*DEFAULT_MESSAGE_WIDTH)/3) *
  174.                     mesg_info->fs->max_bounds.width;
  175.     } else
  176.     wp->pixel_width  = (DEFAULT_MESSAGE_WIDTH *
  177.                     mesg_info->fs->max_bounds.width);
  178.  
  179.     /* Set the new width and height. */
  180.     num_args = 0;
  181.     XtSetArg(args[num_args], XtNwidth,        wp->pixel_width);  num_args++;
  182.     XtSetArg(args[num_args], XtNheight,       wp->pixel_height); num_args++;
  183.     XtSetValues(wp->w, args, num_args);
  184.  
  185.     XtAddEventHandler(wp->w, KeyPressMask, False,
  186.               (XtEventHandler) msgkey, (XtPointer) 0);
  187.     XtAddCallback(wp->w, XtNresizeCallback, mesg_resized, (XtPointer) 0);
  188.  
  189.     /*
  190.      * If we have created our own popup, then realize it so that the
  191.      * viewport is also realized.  Then resize the mesg window.
  192.      */
  193.     if (create_popup) {
  194.     XtRealizeWidget(wp->popup);
  195.     set_message_height(wp, (int) flags.msg_history);
  196.     }
  197. }
  198.  
  199.  
  200. void
  201. destroy_message_window(wp)
  202.     struct xwindow *wp;
  203. {
  204.     if (wp->popup) {
  205.     nh_XtPopdown(wp->popup);
  206.     XtDestroyWidget(wp->popup);
  207.     set_circle_buf(wp->mesg_information, 0);    /* free buffer list */
  208.     free((char *)wp->mesg_information);
  209.     }
  210.     wp->type = NHW_NONE;
  211. }
  212.  
  213.  
  214. /* Redraw message window if new lines have been added. */
  215. void
  216. display_message_window(wp)
  217.     struct xwindow *wp;
  218. {
  219.     if (wp->mesg_information->dirty) redraw_message_window(wp);
  220. }
  221.  
  222.  
  223. /*
  224.  * Append a line of text to the message window.  Split the line if the
  225.  * rendering of the text is too long for the window.
  226.  */
  227. void
  228. append_message(wp, str)
  229.     struct xwindow *wp;
  230.     const char *str;
  231. {
  232.     char *mark, *remainder, buf[BUFSZ];
  233.  
  234.     if (!str) return;
  235.  
  236.     Strcpy(buf, str);    /* we might mark it up */
  237.  
  238.     remainder = buf;
  239.     do {
  240.     mark = remainder;
  241.     remainder = split(mark, wp->mesg_information->fs, wp->pixel_width);
  242.     add_line(wp->mesg_information, mark);
  243.     } while (remainder);
  244. }
  245.  
  246. /* private functions ======================================================= */
  247.  
  248. /*
  249.  * Return the element in the circular linked list just before the given
  250.  * element.
  251.  */
  252. static struct line_element *
  253. get_previous(mark)
  254.     struct line_element *mark;
  255. {
  256.     struct line_element *curr;
  257.  
  258.     if (!mark) return (struct line_element *) 0;
  259.  
  260.     for (curr = mark; curr->next != mark; curr = curr->next)
  261.     ;
  262.     return curr;
  263. }
  264.  
  265.  
  266. /*
  267.  * Set the information buffer size to count lines.  We do this by creating
  268.  * a circular linked list of elements, each of which represents a line of
  269.  * text.  New buffers are created as needed, old ones are freed if they
  270.  * are no longer used.
  271.  */
  272. static void
  273. set_circle_buf(mesg_info, count)
  274.     struct mesg_info_t *mesg_info;
  275.     int count;
  276. {
  277.     int i;
  278.     struct line_element *tail, *curr, *head;
  279.  
  280.     if (count < 0) panic("set_circle_buf: bad count [= %d]", count);
  281.     if (count == mesg_info->num_lines) return;    /* no change in size */
  282.  
  283.     if (count < mesg_info->num_lines) {
  284.     /*
  285.      * Toss num_lines - count line entries from our circular list.
  286.      *  
  287.      * We lose lines from the front (top) of the list.  We _know_
  288.      * the list is non_empty.
  289.      */
  290.     tail = get_previous(mesg_info->head);
  291.     for (i = mesg_info->num_lines - count;    i--; ) {
  292.         curr = mesg_info->head;
  293.         mesg_info->head = curr->next;
  294.         if (curr->line) free(curr->line);
  295.         free((genericptr_t)curr);
  296.     }
  297.     if (count == 0) {
  298.         /* make sure we don't have a dangling pointer */
  299.         mesg_info->head = (struct line_element *) 0;
  300.     } else {
  301.         tail->next = mesg_info->head;    /* link the tail to the head */
  302.     }
  303.     } else {
  304.     /*
  305.      * Add count - num_lines blank lines to the head of the list.
  306.      *
  307.      * Create a separate list, keeping track of the tail.
  308.      */
  309.     for (head = tail = 0, i = 0; i < count - mesg_info->num_lines; i++) {
  310.         curr = (struct line_element *) alloc(sizeof(struct line_element));
  311.         curr->line = 0;
  312.         curr->buf_length = 0;
  313.         curr->str_length = 0;
  314.         if (tail) {
  315.         tail->next = curr;
  316.         tail = curr;
  317.         } else {
  318.         head = tail = curr;
  319.         }
  320.     }
  321.     /*
  322.      * Complete the circle by making the new tail point to the old head
  323.      * and the old tail point to the new head.  If our line count was
  324.      * zero, then make the new list circular.
  325.      */
  326.     if (mesg_info->num_lines) {
  327.         curr = get_previous(mesg_info->head);/* get end of old list */
  328.  
  329.         tail->next = mesg_info->head;    /* new tail -> old head */
  330.         curr->next = head;            /* old tail -> new head */
  331.     } else {
  332.         tail->next = head;
  333.     }
  334.     mesg_info->head = head;
  335.     }
  336.  
  337.     mesg_info->num_lines = count;
  338.     /* Erase the line on a resize. */
  339.     mesg_info->last_pause = (struct line_element *) 0;
  340. }
  341.  
  342.  
  343. /*
  344.  * Make sure the given string is shorter than the given pixel width.  If
  345.  * not, back up from the end by words until we find a place to split.
  346.  */
  347. static char *
  348. split(s, fs, pixel_width)
  349.     char *s;
  350.     XFontStruct *fs;        /* Font for the window. */
  351.     Dimension pixel_width;
  352. {
  353.     char save, *end, *remainder;
  354.  
  355.     save = '\0';
  356.     remainder = 0;
  357.     end = eos(s);    /* point to null at end of string */
  358.  
  359.     /* assume that if end == s, XXXXXX returns 0) */
  360.     while ((Dimension) XTextWidth(fs, s, (int) strlen(s)) > pixel_width) {
  361.     *end-- = save;
  362.     while (*end != ' ') {
  363.         if (end == s) panic("split: eos!");
  364.         --end;
  365.     }
  366.     save = *end;
  367.     *end = '\0';
  368.     remainder = end + 1;
  369.     }
  370.     return remainder;
  371. }
  372.  
  373. /*
  374.  * Add a line of text to the window.  The first line in the curcular list
  375.  * becomes the last.  So all we have to do is copy the new line over the
  376.  * old one.  If the line buffer is too small, then allocate a new, larger
  377.  * one.
  378.  */
  379. static void
  380. add_line(mesg_info, s)
  381.     struct mesg_info_t *mesg_info;
  382.     const char *s;
  383. {
  384.     register struct line_element *curr = mesg_info->head;
  385.     register int new_line_length = strlen(s);
  386.  
  387.     if (new_line_length + 1 > curr->buf_length) {
  388.     if (curr->line) free(curr->line);    /* free old line */
  389.  
  390.     curr->buf_length = new_line_length + 1;
  391.     curr->line = (char *) alloc(curr->buf_length);
  392.     }
  393.  
  394.     Strcpy(curr->line, s);            /* copy info */
  395.     curr->str_length = new_line_length;        /* save string length */
  396.  
  397.     mesg_info->head = mesg_info->head->next;    /* move head to next line */
  398.     mesg_info->dirty = True;            /* we have undrawn lines */
  399. }
  400.  
  401.  
  402. /*
  403.  * Save a position in the text buffer so we can draw a line to seperate
  404.  * text from the last time this function was called.
  405.  *
  406.  * Save the head position, since it is the line "after" the last displayed
  407.  * line in the message window.  The window redraw routine will draw a
  408.  * line above this saved pointer.
  409.  */
  410. void
  411. set_last_pause(wp)
  412.     struct xwindow *wp;
  413. {
  414.     register struct mesg_info_t *mesg_info = wp->mesg_information;
  415.  
  416. #ifdef ERASE_LINE
  417.     /*
  418.      * If we've erased the pause line and haven't added any new lines,
  419.      * don't try to erase the line again.
  420.      */
  421.     if (!mesg_info->last_pause
  422.                 && mesg_info->last_pause_head == mesg_info->head)
  423.     return;
  424.  
  425.     if (mesg_info->last_pause == mesg_info->head) {
  426.     /* No new messages in last turn.  Redraw window to erase line. */
  427.     mesg_info->last_pause = (struct line_element *) 0;
  428.     mesg_info->last_pause_head = mesg_info->head;
  429.     redraw_message_window(wp);
  430.     } else {
  431. #endif
  432.     mesg_info->last_pause = mesg_info->head;
  433. #ifdef ERASE_LINE
  434.     }
  435. #endif
  436. }
  437.  
  438.  
  439. static void
  440. redraw_message_window(wp)
  441.     struct xwindow *wp;
  442. {
  443.     struct mesg_info_t *mesg_info = wp->mesg_information;
  444.     register struct line_element *curr;
  445.     register int row;
  446.  
  447.     /*
  448.      * Do this the cheap and easy way.  Clear the window and just redraw
  449.      * the whole thing.
  450.      *
  451.      * This could be done more effecently with one call to XDrawText() instead
  452.      * of many calls to XDrawString().  Maybe later.
  453.      */
  454.     XClearWindow(XtDisplay(wp->w), XtWindow(wp->w));
  455.  
  456.     /* For now, just update the whole shootn' match. */
  457.     for (row = 0, curr = mesg_info->head;
  458.             row < mesg_info->num_lines; row++, curr = curr->next) {
  459.  
  460.     register int y_base = row * mesg_info->char_height;
  461.  
  462.     XDrawString(XtDisplay(wp->w), XtWindow(wp->w),
  463.                 mesg_info->gc,
  464.         mesg_info->char_lbearing,
  465.                 mesg_info->char_ascent + y_base,
  466.         curr->line,
  467.         curr->str_length);
  468.  
  469.     /*
  470.      * This draws a line at the _top_ of the line of text pointed to by
  471.      * mesg_info->last_pause.
  472.      */
  473.     if (appResources.message_line && curr == mesg_info->last_pause) {
  474.         XDrawLine(XtDisplay(wp->w), XtWindow(wp->w),
  475.                 mesg_info->gc,
  476.         0, y_base, wp->pixel_width, y_base);
  477.     }
  478.     }
  479.  
  480.     mesg_info->dirty = False;
  481. }
  482.  
  483.  
  484. /*
  485.  * Check the size of the viewport.  If it has shrunk, then we want to
  486.  * move the vertical slider to the bottom.
  487.  */
  488. static void
  489. mesg_check_size_change(wp)
  490.     struct xwindow *wp;
  491. {
  492.     struct mesg_info_t *mesg_info = wp->mesg_information;
  493.     Arg arg[2];
  494.     Dimension new_width, new_height;
  495.     Widget viewport;
  496.  
  497.     viewport = XtParent(wp->w);
  498.  
  499.     XtSetArg(arg[0], XtNwidth,  &new_width);
  500.     XtSetArg(arg[1], XtNheight, &new_height);
  501.     XtGetValues(viewport, arg, TWO);
  502.  
  503.     /* Only move slider to bottom if new size is smaller. */
  504.     if (new_width < mesg_info->viewport_width
  505.             || new_height < mesg_info->viewport_height) {
  506.     set_message_slider(wp);
  507.     }
  508.  
  509.     mesg_info->viewport_width = new_width;
  510.     mesg_info->viewport_height = new_height;
  511. }
  512.  
  513.  
  514. /* Event handler for message window expose events. */
  515. /*ARGSUSED*/
  516. static void
  517. mesg_exposed(w, event)
  518.     Widget w;
  519.     XExposeEvent *event;    /* unused */
  520. {
  521.     struct xwindow *wp;
  522.  
  523.     if (!XtIsRealized(w)) return;
  524.     wp = find_widget(w);
  525.     mesg_check_size_change(wp);
  526.     redraw_message_window(wp);
  527. }
  528.  
  529.  
  530. static void
  531. get_gc(w, mesg_info)
  532.     Widget w;
  533.     struct mesg_info_t *mesg_info;
  534. {
  535.     XGCValues values;
  536.     XtGCMask mask = GCFunction | GCForeground | GCBackground | GCFont;
  537.     Pixel fgpixel, bgpixel;
  538.     Arg arg[2];
  539.  
  540.     XtSetArg(arg[0], XtNforeground, &fgpixel);
  541.     XtSetArg(arg[1], XtNbackground, &bgpixel);
  542.     XtGetValues(w, arg, TWO);
  543.  
  544.     values.foreground = fgpixel;
  545.     values.background = bgpixel;
  546.     values.function   = GXcopy;
  547.     values.font       = WindowFont(w);
  548.     mesg_info->gc = XtGetGC(w, mask, &values);
  549. }
  550.  
  551. /*
  552.  * Handle resizes on a message window.  Correct saved pixel height and width.
  553.  * Adjust circle buffer to accomidate the new size.
  554.  * 
  555.  * Problem:  If the resize decreases the width of the window such that
  556.  * some lines are now longer than the window, they will be cut off by
  557.  * X itself.  All new lines will be split to the new size, but the ends
  558.  * of the old ones will not be seen again unless the window is lengthened.
  559.  * I don't deal with this problem because it isn't worth the trouble.
  560.  */
  561. /* ARGSUSED */
  562. static void
  563. mesg_resized(w, client_data, call_data)
  564.     Widget w;
  565.     XtPointer call_data, client_data;
  566. {
  567.     Arg args[4];
  568.     Cardinal num_args;
  569.     Dimension pixel_width, pixel_height;
  570.     struct xwindow *wp;
  571. #ifdef VERBOSE
  572.     int old_lines;
  573.  
  574.     old_lines = wp->mesg_information->num_lines;;
  575. #endif
  576.  
  577.     num_args = 0;
  578.     XtSetArg(args[num_args], XtNwidth,  &pixel_width);   num_args++;
  579.     XtSetArg(args[num_args], XtNheight, &pixel_height);  num_args++;
  580.     XtGetValues(w, args, num_args);
  581.  
  582.     wp = find_widget(w);
  583.     wp->pixel_width  = pixel_width;
  584.     wp->pixel_height = pixel_height;
  585.  
  586.     set_circle_buf(wp->mesg_information,
  587.             (int) pixel_height / wp->mesg_information->char_height);
  588.  
  589. #ifdef VERBOSE
  590.     printf("Message resize.  Pixel: width = %d, height = %d;  Lines: old = %d, new = %d\n", 
  591.     pixel_width,
  592.     pixel_height,
  593.     old_lines,
  594.     wp->mesg_information->num_lines);
  595. #endif
  596. }
  597.